import puppeteer, { Browser, Page, HTTPResponse } from 'puppeteer';
import { promises as fs } from 'fs';
import path from 'path';
import { Logger } from './logger';

let browser: Browser = null;

/**
 * 浏览器会话选项
 */
export interface BrowserSessionOptions {
  /** 页面超时时间（秒） */
  timeout?: number;
  /** 是否等待网络空闲 */
  waitUntilNetworkIdle?: boolean;
  /** 自定义请求拦截器 */
  requestInterceptor?: (request: any) => void;
  /** 自定义响应拦截器 */
  responseInterceptor?: (response: HTTPResponse) => void;
}

/**
 * 浏览器会话回调函数
 */
export type BrowserSession = {
  browser: Browser;
  page: Page;
  utils: BrowserSessionUtils;
};

/**
 * 浏览器会话工具集
 */
export interface BrowserSessionUtils {
  /** 等待指定URL的响应 */
  waitForResponse: (url: string) => Promise<HTTPResponse>;
  /** 查找网络请求 */
  findRequest: (url: string) => { request: any; response: HTTPResponse };
  /** 关闭页面 */
  close?: () => Promise<void>;
  /** 所有网络请求 */
  networkRequests: any[];
}

/** 启动浏览器 */
export async function startBrowser(): Promise<Browser> {
  if (browser) {
    return Promise.resolve(browser);
  }

  // 定义用户数据目录以持久化登录状态（如 cookies）
  const userDataDir = path.join(process.cwd(), 'design-assets', 'user-data');

  // 确保目录存在
  await fs.mkdir(userDataDir, { recursive: true });

  browser = await puppeteer.launch({
    headless: false,
    userDataDir, // 指定用户数据目录
    args: ['--no-sandbox', '--disable-setuid-sandbox'],
  });

  browser.on('disconnected', () => {
    Logger.info('浏览器已断开连接');

    browser = undefined; // 清除浏览器实例
  });

  return browser;
}

/**
 * 创建浏览器会话 - 通用的浏览器操作封装
 * @param url 要访问的URL
 * @param callback 回调函数，接收browser、page和工具集
 * @param options 可选配置
 * @returns 回调函数的返回值
 */
export async function createBrowserSession(
  url: string,
  options: BrowserSessionOptions = {}
): Promise<BrowserSession> {
  const startTime = Date.now();

  Logger.info(`开始浏览器会话: ${url}`);

  // 默认选项
  const opts: Required<BrowserSessionOptions> = {
    timeout: options.timeout ?? 30,
    waitUntilNetworkIdle: options.waitUntilNetworkIdle ?? true,
    requestInterceptor: options.requestInterceptor,
    responseInterceptor: options.responseInterceptor,
  };

  const browser = await startBrowser();
  const page = await browser.newPage();

  // 设置超时
  page.setDefaultTimeout(opts.timeout * 1000);

  try {
    // 创建页面请求池和工具集
    const utils = usePageRequestPool(page);

    // 添加自定义拦截器
    if (opts.requestInterceptor) {
      page.on('request', opts.requestInterceptor);
    }

    if (opts.responseInterceptor) {
      page.on('response', opts.responseInterceptor);
    }

    // 访问页面
    const waitUntil = opts.waitUntilNetworkIdle ? 'networkidle2' : 'load';

    await page.goto(url, { waitUntil });

    // 执行用户回调
    Logger.info(`浏览器会话完成: ${url} (${Date.now() - startTime}ms)`);

    utils.close = () => page.close();

    return {
      browser,
      page,
      utils,
    };
  } catch (error) {
    const duration = Date.now() - startTime;
    Logger.error(`浏览器会话失败: ${url} (${duration}ms)`, error);
    throw error;
  }
}

/** 使用页面请求池 */
export function usePageRequestPool(page: Page): BrowserSessionUtils {
  const networkRequests: any[] = [];

  const responsePool = new WeakMap();

  const baseOnResponse = response => {
    const currentUrl = response.url();

    const request = networkRequests.find(req => req.url === currentUrl);

    responsePool.set(request, response);
  };

  const listeners = [baseOnResponse];

  page.on('request', request => {
    networkRequests.push({
      url: request.url(),
      method: request.method(),
    });
  });

  page.on('response', response => {
    listeners.forEach(listener => {
      listener(response);
    });
  });

  return {
    networkRequests,

    findRequest(url) {
      const request = networkRequests.find(req => req.url.includes(url));

      return {
        request,
        response: responsePool.get(request),
      };
    },

    waitForResponse(url) {
      return new Promise<HTTPResponse>((resolve, reject) => {
        const timeout = setTimeout(() => {
          remove();

          reject(new Error(`Request timed out: ${url}`));
        }, 30000);

        const remove = () => {
          listeners.splice(listeners.indexOf(onResponse), 1);
        };

        const onResponse = response => {
          if (response && response.url().includes(url)) {
            clearTimeout(timeout);
            resolve(response);

            remove();
          }
        };

        networkRequests.forEach(req => {
          onResponse(responsePool.get(req));
        });

        listeners.push(onResponse);
      });
    },
  };
}

/**
 * 通用浏览器工具函数
 */
export class BrowserUtils {
  /**
   * 截取网页截图
   * @param url 网页地址
   * @param options 截图选项
   * @returns 截图的base64数据
   */
  static async captureScreenshot(
    url: string,
    options: { fullPage?: boolean; quality?: number } = {}
  ): Promise<string> {
    const { page } = await createBrowserSession(url, { timeout: 30 });

    const screenshot = await page.screenshot({
      type: 'png',
      fullPage: options.fullPage ?? true,
      quality: options.quality ?? 90,
      encoding: 'base64',
    });

    return screenshot;
  }

  /**
   * 获取页面基本信息
   * @param url 网页地址
   * @returns 页面基本信息
   */
  static async getPageInfo(url: string): Promise<{
    title: string;
    description: string;
    keywords: string;
    url: string;
    networkRequests: number;
  }> {
    const { utils, page } = await createBrowserSession(url, { timeout: 20 });

    const title = await page.title();

    const description = await page
      .$eval('meta[name="description"]', el => el.getAttribute('content'))
      .catch(() => '');

    const keywords = await page
      .$eval('meta[name="keywords"]', el => el.getAttribute('content'))
      .catch(() => '');

    return {
      title,
      description,
      keywords,
      url: page.url(),
      networkRequests: utils.networkRequests.length,
    };
  }

  /**
   * 等待并获取特定API响应数据
   * @param url 网页地址
   * @param apiPattern API路径模式
   * @param timeout 超时时间
   * @returns API响应数据
   */
  static async waitForApiData(url: string, apiPattern: string, timeout: number = 30) {
    try {
      const { utils } = await createBrowserSession(url, {
        timeout,
      });

      const response = await utils.waitForResponse(apiPattern);

      const data = response && (await response.json());

      return {
        data,
        utils,
      };
    } catch (error) {
      Logger.error(`等待API数据失败: ${url} (${apiPattern})`, error);

      return null;
    }
  }

  /**
   * 执行自定义JavaScript代码并返回结果
   * @param url 网页地址
   * @param script 要执行的JavaScript代码
   * @param timeout 超时时间
   * @returns 执行结果
   */
  static async executeScript<T = any>(
    url: string,
    script: string,
    timeout: number = 30
  ): Promise<T> {
    const { page } = await createBrowserSession(url, { timeout });

    const result = await page.evaluate(script);

    return result as T;
  }

  // /**
  //  * 模拟用户交互并获取结果
  //  * @param url 网页地址
  //  * @param interactions 交互步骤
  //  * @param timeout 超时时间
  //  * @returns 交互结果
  //  */
  // static async simulateUserInteraction<T = any>(
  //   url: string,
  //   interactions: (page: Page, utils: BrowserSessionUtils) => Promise<T>,
  //   timeout: number = 60
  // ): Promise<T> {
  //   const { page, utils } = await createBrowserSession(url, {
  //     timeout,
  //     waitUntilNetworkIdle: false,
  //   });

  //   return await interactions(page, utils);
  // }
}
